import type {
  LivePayload,
  GameCfg,
  Team,
  Match,
  HistoryItem,
  UpcomingItem,
  PlayerRef,
  Bracket,
} from './types';

// 直播数据 URL
const DEFAULT_LIVE_URL = 'https://assets.box3.fun/u226/1/liveInfo.json';

const DEFAULT_LIVE_URL_TEST =
  'https://assets.box3.fun/u226/1/liveInfoTest.json';

/**
 * 将秒转换为毫秒
 */
function secToMs(sec: number): number {
  return sec * 1000;
}

/**
 * 安全地将 Unix 秒时间戳转换为 Date
 */
function safeDateFromUnixSeconds(sec: number): Date {
  return new Date(secToMs(sec));
}

// 简单签名函数：用于变更检测（稳定 JSON 序列化）
function safeSignature(obj: unknown): string {
  try {
    return JSON.stringify(obj);
  } catch {
    return '';
  }
}

/**
 * LiveEvent（配置侧数据提供者）
 *
 * 职责：负责拉取并维护“配置好的比赛信息”（赛程、队伍、地图、历史、预告等）。
 * - 数据来源：固定/可配置的 liveInfo.json（生产/测试/自定义）。
 * - 能力：提供查询当前赛程、下一场、上场名单等便捷方法。
 *
 * 与 LiveUpdater 的关系：
 * - LiveEvent 只管理“配置数据”，不直接进行数据上报。
 * - LiveUpdater 接收实时对战数据（比分、玩家扩展），并可调用
 *   LiveEvent 的方法（如 `getMatchesSortedByStart()`、`getCurrentMatch()`）
 *   来自动选择“历史+当前”的赛程构建 payload。
 */
export default class LiveEvent {
  private static instance: LiveEvent | null = null;
  private static readonly DEFAULT_DATA: LivePayload = {
    type: 'Live',
    env: 'production',
    title: '',
    description: '',
    roomId: 0,
    startTime: 0,
    endTime: 0,
    cover: '',
    history: [],
    upcoming: [],
  };

  private data: LivePayload = LiveEvent.DEFAULT_DATA;
  private timer: ReturnType<typeof setTimeout> | null = null;
  private stopped = false;
  private listeners = new Set<(data: LivePayload) => void>();
  private lastSignature = '';
  // 数据源 & 拉取
  private liveUrl: string = DEFAULT_LIVE_URL;
  private fetcher: (url: string) => Promise<{ json: () => Promise<unknown> }> =
    (url) => http.fetch(url);
  // 缓存索引，加速查询
  private teamsMap = new Map<number, Team>();
  private playersMap = new Map<number, PlayerRef>();
  // 错误与玩家级监听
  private errorListeners = new Set<(err: unknown) => void>();
  private playerListeners = new Map<
    number,
    Set<(ref: PlayerRef | undefined) => void>
  >();

  /**
   * 构造函数
   * @param payload 直播数据主载体
   */
  private constructor(initial?: LivePayload) {
    if (initial) {
      this.data = initial;
    }
    this.rebuildIndexes();
    this.startPolling();
  }

  /**
   * 原地更新内部数据（内部使用）
   */
  private update(payload: LivePayload): void {
    const nextSig = safeSignature(payload);
    if (nextSig !== this.lastSignature) {
      // 写入新数据并重建索引
      this.data = payload;
      this.lastSignature = nextSig;
      this.rebuildIndexes();
      // 触发总更新
      this.emitUpdate();
    }
  }

  /** 获取单例；首次调用会自动开始轮询（默认 30s） */
  static getInstance(): LiveEvent {
    if (LiveEvent.instance) {
      return LiveEvent.instance;
    }
    const inst = new LiveEvent();
    LiveEvent.instance = inst;
    return inst;
  }

  private intervalMs = 2000;
  // 全局时间覆盖（用于测试/演示）：若设置，则所有支持 nowSec 的方法优先使用该值
  private _nowSecOverride: number | undefined;

  /** 统一时间解析：优先使用全局覆盖，其次入参，最后系统时间 */
  private resolveNowSec(nowSec?: number): number {
    if (typeof this._nowSecOverride === 'number') {
      return this._nowSecOverride;
    }
    if (typeof nowSec === 'number') {
      return nowSec;
    }
    return Math.floor(Date.now() / 1000);
  }

  /** 设置全局 nowSec 覆盖；传 undefined 取消覆盖 */
  setNowSecOverride(sec?: number): void {
    this._nowSecOverride = typeof sec === 'number' ? sec : undefined;
  }

  /** 取消覆盖，恢复实时 nowSec */
  useRealTime(): void {
    this._nowSecOverride = undefined;
  }

  /** 启动轮询（内部使用） */
  private startPolling(): void {
    const tick = async () => {
      if (this.stopped) {
        return;
      }
      try {
        await this.fetchOnce();
      } catch (err) {
        // 忽略错误，按周期重试
        this.emitError(err);
      } finally {
        if (!this.stopped) {
          this.timer = setTimeout(tick, this.intervalMs);
        }
      }
    };
    void tick();
  }

  /** 立即拉取一次（公开） */
  async refreshNow(): Promise<void> {
    if (this.stopped) {
      return;
    }
    try {
      await this.fetchOnce();
    } catch (err) {
      this.emitError(err);
      // 向外抛出以便上层捕获（可选）
      throw err;
    }
  }

  /** 单次拉取逻辑（内部共用） */
  private async fetchOnce(): Promise<void> {
    const res = await this.fetcher(this.liveUrl + '?t=' + Date.now());
    const payload = (await res.json()) as LivePayload;
    this.update(payload);
  }

  /** 停止轮询（如不需要可不调用） */
  stop(): void {
    this.stopped = true;
    if (this.timer) {
      clearTimeout(this.timer);
      this.timer = null;
    }
  }

  /** 注入自定义 fetcher（例如用于测试或自定义请求头） */
  setFetcher(
    fetcher: (url: string) => Promise<{ json: () => Promise<unknown> }>
  ): void {
    this.fetcher = fetcher;
  }

  /**
   * 切换为测试数据源（完整版、便于测试/联调）。
   * 注意：发布到正式环境前请切回生产源。
   * 生产源：https://assets.box3.fun/u226/1/liveInfo.json
   * 测试源：https://assets.box3.fun/u226/1/liveInfoTest.json
   */
  async useTestData(enable: boolean): Promise<void> {
    this.liveUrl = enable ? DEFAULT_LIVE_URL_TEST : DEFAULT_LIVE_URL;
    await this.fetchOnce();
  }

  /**
   * 切换为自定义数据源 URL。
   * @param url 自定义数据源的 URL
   */
  async useCustomUrl(url: string): Promise<void> {
    this.liveUrl = url;
    await this.fetchOnce();
  }

  /** 是否正在使用测试数据源 */
  isUsingTestData(): boolean {
    return this.liveUrl === DEFAULT_LIVE_URL_TEST;
  }

  /** 恢复轮询（与 stop 相反） */
  resume(): void {
    if (!this.stopped) {
      return;
    }
    this.stopped = false;
    this.startPolling();
  }

  // --- Events ---
  /** 订阅数据更新事件，如数据更新则触发 */
  onUpdate(cb: (data: LivePayload) => void): () => void {
    this.listeners.add(cb);
    // 返回取消订阅函数
    return () => this.offUpdate(cb);
  }

  /** 取消订阅 */
  offUpdate(cb: (data: LivePayload) => void): void {
    this.listeners.delete(cb);
  }

  /** 触发更新事件（内部使用） */
  private emitUpdate(): void {
    for (const cb of this.listeners) {
      try {
        cb(this.data);
      } catch {
        // 忽略单个监听器异常
      }
    }
  }

  /** 返回一个 Promise，在下一次 onUpdate 触发时 resolve（可用于一次性等待） */
  waitForNextUpdate(timeoutMs?: number): Promise<LivePayload> {
    return new Promise<LivePayload>((resolve, reject) => {
      let timer: ReturnType<typeof setTimeout> | null = null;
      const off = this.onUpdate((data) => {
        off();
        if (timer) {
          clearTimeout(timer);
        }
        resolve(data);
      });
      if (typeof timeoutMs === 'number' && timeoutMs > 0) {
        timer = setTimeout(() => {
          off();
          reject(new Error('waitForNextUpdate timeout'));
        }, timeoutMs);
      }
    });
  }

  /** 错误事件 */
  onError(cb: (err: unknown) => void): () => void {
    this.errorListeners.add(cb);
    return () => this.offError(cb);
  }
  offError(cb: (err: unknown) => void): void {
    this.errorListeners.delete(cb);
  }
  private emitError(err: unknown): void {
    for (const cb of this.errorListeners) {
      try {
        cb(err);
      } catch {}
    }
  }

  /** 订阅某个玩家的数据变化（例如名单变更、新增/消失） */
  onPlayerUpdate(
    playerId: number,
    cb: (ref: PlayerRef | undefined) => void
  ): () => void {
    let set = this.playerListeners.get(playerId);
    if (!set) {
      set = new Set();
      this.playerListeners.set(playerId, set);
    }
    set.add(cb);
    return () => this.offPlayerUpdate(playerId, cb);
  }
  offPlayerUpdate(
    playerId: number,
    cb: (ref: PlayerRef | undefined) => void
  ): void {
    const set = this.playerListeners.get(playerId);
    if (set) {
      set.delete(cb);
      if (set.size === 0) {
        this.playerListeners.delete(playerId);
      }
    }
  }

  /** 重建 teamsMap / playersMap 索引 */
  private rebuildIndexes(): void {
    this.teamsMap.clear();
    this.playersMap.clear();
    for (const t of this.getTeams()) {
      this.teamsMap.set(t.id, t);
      for (const p of t.players) {
        if (!this.playersMap.has(p.id)) {
          this.playersMap.set(p.id, p);
        }
      }
    }
  }

  // --- Top-level ---
  /** 获取直播类型 */
  getType(): LivePayload['type'] {
    return this.data.type;
  }

  /** 获取运行环境 */
  getEnv(): string {
    return this.data.env;
  }

  /** 获取直播标题 */
  getTitle(): string {
    return this.data.title;
  }

  /** 获取直播描述 */
  getDescription(): string {
    return this.data.description;
  }

  /** 获取直播间ID */
  getRoomId(): number {
    return this.data.roomId;
  }

  /** 获取封面URL */
  getCover(): string {
    return this.data.cover;
  }

  /** 获取开播时间（秒） */
  getStartTime(): number {
    return this.data.startTime;
  }

  /** 获取结束时间（秒） */
  getEndTime(): number {
    return this.data.endTime;
  }

  /** 获取开播时间（Date） */
  getStartDate(): Date {
    return safeDateFromUnixSeconds(this.data.startTime);
  }

  /** 获取结束时间（Date） */
  getEndDate(): Date {
    return safeDateFromUnixSeconds(this.data.endTime);
  }

  /**
   * 当前是否处于直播时间区间
   * @param nowSec 当前时间（秒），默认 Math.floor(Date.now()/1000)
   */
  isLive(nowSec: number = Math.floor(Date.now() / 1000)): boolean {
    nowSec = this.resolveNowSec(nowSec);
    const start = this.data.startTime;
    const end = this.data.endTime;
    return nowSec >= start && nowSec <= end;
  }

  // --- GameCfg ---
  /** 获取赛事配置（可能不存在） */
  getGameCfg(): GameCfg | undefined {
    return this.data.gameCfg;
  }

  /** 获取赛事LOGO URL（可能不存在） */
  getLogo(): string | undefined {
    return this.data.gameCfg?.logo;
  }

  /** 获取参赛队伍列表（gameCfg 不存在时返回空数组） */
  getTeams(): Team[] {
    return this.data.gameCfg?.teams ?? [];
  }

  /** 获取比赛地图列表（gameCfg.matchMaps，不存在时返回空数组） */
  getMatchMaps() {
    return this.data.gameCfg?.matchMaps ?? [];
  }

  /** 获取解说员ID列表（gameCfg 不存在时返回空数组） */
  getCommentators(): number[] {
    return this.data.gameCfg?.commentators ?? [];
  }

  /** 获取中台成员ID列表（gameCfg 不存在时返回空数组） */
  getOperators(): number[] {
    return this.data.gameCfg?.operators ?? [];
  }

  /** 获取晋级图信息（gameCfg 不存在时返回 null） */
  getBracket(): Bracket | null {
    return this.data.gameCfg?.bracket ?? null;
  }

  /** 获取当前界面类型
   * @param commentatorsEntity 解说员玩家实体
   * - live_mini: 小窗副屏直播
   * - live: 大窗主屏直播
   */
  getScreenType(
    commentatorsEntity: GamePlayerEntity
  ): 'live_mini' | 'live' | undefined {
    const from = commentatorsEntity.player.url.searchParams.get('from');
    if (from === 'live_mini') {
      return 'live_mini';
    }
    if (from === 'live') {
      return 'live';
    }
    return undefined;
  }

  /** 获取界面是否隐藏了玩家UI
   * @param entity 玩家实体
   * - true: 隐藏UI
   * - false: 显示UI
   */
  getPlayerUiHidden(entity: GamePlayerEntity): boolean {
    const from = entity.player.url.searchParams.get('isHidePlayUI');
    return from === 'true';
  }

  /** 获取小窗播放器尺寸（gameCfg 不存在时返回 null） */
  getMiniPlayer(): { width: number; height: number } | undefined {
    return this.data.gameCfg?.miniPlayer;
  }

  /** 获取当前比赛在 matches 列表中的下标（找不到则返回 -1） */
  getCurrentMatchIndex(nowSec: number = Math.floor(Date.now() / 1000)): number {
    nowSec = this.resolveNowSec(nowSec);
    const cur = this.getCurrentMatch(nowSec);
    if (!cur) {
      return -1;
    }
    const list = this.getMatchesSortedByStart();
    return list.findIndex((m) => m.startTime === cur.startTime);
  }

  /** 按下标读取比赛地图（超界或不存在时返回 undefined） */
  getMatchMapByIndex(index: number) {
    const maps = this.getMatchMaps();
    if (index < 0 || index >= maps.length) {
      return undefined;
    }
    return maps[index];
  }

  /** 获取当前比赛对应的比赛地图（按 matches 排序下标与 matchMaps 对齐） */
  getCurrentMatchMap(nowSec: number = Math.floor(Date.now() / 1000)) {
    nowSec = this.resolveNowSec(nowSec);
    const idx = this.getCurrentMatchIndex(nowSec);
    if (idx < 0) {
      return undefined;
    }
    return this.getMatchMapByIndex(idx);
  }

  /** 按队伍ID查找队伍 */
  getTeamById(id: number): Team | undefined {
    return this.teamsMap.get(id) ?? this.getTeams().find((t) => t.id === id);
  }

  /** 按队伍名称查找队伍 */
  getTeamByName(name: string): Team | undefined {
    return this.getTeams().find((t) => t.name === name);
  }

  /** 获取指定队伍的队员ID列表 */
  getTeamPlayerIds(teamId: number): number[] {
    const t = this.getTeamById(teamId);
    return t ? t.players.map((p) => p.id) : [];
  }

  /** 获取所有队伍的去重队员ID */
  getAllPlayerIdsFromTeams(): number[] {
    const ids = new Set<number>();
    for (const t of this.getTeams()) {
      for (const p of t.players) {
        ids.add(p.id);
      }
    }
    return [...ids];
  }

  /** 获取所有队伍的玩家引用（去重） */
  getAllPlayerRefsFromTeams(): PlayerRef[] {
    return [...this.playersMap.values()];
  }

  /**
   * 获取所有队伍的所有玩家（基于 teams 配置，已去重）
   */
  getAllPlayers(): PlayerRef[] {
    return this.getAllPlayerRefsFromTeams();
  }

  /** 获取全部比赛安排（gameCfg 不存在时返回空数组） */
  getMatches(): Match[] {
    return this.data.gameCfg?.matches ?? [];
  }

  /** 按开始时间升序返回比赛列表 */
  getMatchesSortedByStart(): Match[] {
    return [...this.getMatches()].sort((a, b) => a.startTime - b.startTime);
  }

  /** 获取第一场比赛 */
  getFirstMatch(): Match | undefined {
    return this.getMatchesSortedByStart()[0];
  }

  /** 获取最后一场比赛 */
  getLastMatch(): Match | undefined {
    const list = this.getMatchesSortedByStart();
    return list[list.length - 1];
  }

  /** 查找指定开赛时间（秒）的比赛 */
  getMatchAt(unixSec: number): Match | undefined {
    return this.getMatches().find((m) => m.startTime === unixSec);
  }

  /**
   * 获取下一场比赛（严格在当前时间之后的第一场）。
   * @param nowSec 当前时间（秒），默认 Math.floor(Date.now()/1000)
   */
  getNextMatch(
    nowSec: number = Math.floor(Date.now() / 1000)
  ): Match | undefined {
    nowSec = this.resolveNowSec(nowSec);
    return this.getMatchesSortedByStart().find((m) => m.startTime > nowSec);
  }

  /**
   * 获取“当前进行中的比赛”。
   * 规则：按开始时间排序后，满足 start <= now < next.start 的那一场；
   * 若已到最后一场且 now >= 最后一场的 start，则返回最后一场。
   * @param nowSec 当前时间（秒），默认 Math.floor(Date.now()/1000)
   */
  getCurrentMatch(
    nowSec: number = Math.floor(Date.now() / 1000)
  ): Match | undefined {
    nowSec = this.resolveNowSec(nowSec);
    const list = this.getMatchesSortedByStart();
    for (let i = 0; i < list.length; i++) {
      const cur = list[i];
      const next = list[i + 1];
      const curStart = cur.startTime;
      const nextStart = next?.startTime ?? Number.POSITIVE_INFINITY;
      if (nowSec >= curStart && nowSec < nextStart) {
        return cur;
      }
    }
    return undefined;
  }

  /** 当前进行中的比赛已进行时长（秒）；无进行中比赛则返回 undefined */
  getCurrentMatchDurationSec(
    nowSec: number = Math.floor(Date.now() / 1000)
  ): number | undefined {
    nowSec = this.resolveNowSec(nowSec);
    const cur = this.getCurrentMatch(nowSec);
    if (!cur) {
      return undefined;
    }
    return Math.max(0, nowSec - cur.startTime);
  }

  /** 距离下一场比赛开始的剩余时间（秒）；没有下一场返回 undefined */
  getTimeToNextMatchSec(
    nowSec: number = Math.floor(Date.now() / 1000)
  ): number | undefined {
    nowSec = this.resolveNowSec(nowSec);
    const next = this.getNextMatch(nowSec);
    if (!next) {
      return undefined;
    }
    return Math.max(0, next.startTime - nowSec);
  }

  /** 获取比赛的双方队伍名称 */
  getMatchTeams(match: Match): { teamA: string; teamB: string } {
    return { teamA: match.teamA.name, teamB: match.teamB.name };
  }

  /** 获取比赛的双方上场队员 */
  getMatchPlayers(match: Match): { teamA: PlayerRef[]; teamB: PlayerRef[] } {
    return { teamA: match.teamA.players, teamB: match.teamB.players };
  }

  /**
   * 获取当前比赛双方队伍对象（通过名称匹配 Team，可能找不到则为 undefined）
   */
  getCurrentTeams(nowSec: number = Math.floor(Date.now() / 1000)): {
    teamA?: Team;
    teamB?: Team;
  } {
    nowSec = this.resolveNowSec(nowSec);
    const cur = this.getCurrentMatch(nowSec);
    if (!cur) {
      return {};
    }
    return {
      teamA: this.getTeamByName(cur.teamA.name),
      teamB: this.getTeamByName(cur.teamB.name),
    };
  }

  /** 返回队伍的队员列表（Team 配置层面的成员） */
  getTeamPlayers(teamId: number): PlayerRef[] {
    const t = this.getTeamById(teamId);
    return t ? t.players : [];
  }

  /** 获取“当前进行中的比赛”的所有上场队员（若无进行中比赛则空数组） */
  getCurrentMatchPlayerRefs(
    nowSec: number = Math.floor(Date.now() / 1000)
  ): PlayerRef[] {
    nowSec = this.resolveNowSec(nowSec);
    const cur = this.getCurrentMatch(nowSec);
    if (!cur) {
      return [];
    }
    const { teamA, teamB } = this.getMatchPlayers(cur);
    return [...teamA, ...teamB];
  }

  // --- 玩家/队伍便捷 ---
  /** 指定玩家是否在当前进行中的比赛上场 */
  isPlayerInCurrentMatch(
    playerId: number,
    nowSec: number = Math.floor(Date.now() / 1000)
  ): boolean {
    nowSec = this.resolveNowSec(nowSec);
    return this.getCurrentMatchPlayerRefs(nowSec).some(
      (p) => p.id === playerId
    );
  }

  /** 根据玩家ID查找其所在队伍（基于 teams 配置），找不到返回 undefined */
  getTeamByPlayerId(playerId: number): Team | undefined {
    for (const t of this.getTeams()) {
      if (t.players.some((p) => p.id === playerId)) {
        return t;
      }
    }
    return undefined;
  }

  /** 返回玩家所在队伍ID（找不到返回 undefined） */
  getPlayerTeamId(playerId: number): number | undefined {
    return this.getTeamByPlayerId(playerId)?.id;
  }

  /** 判断玩家是否属于指定队伍（基于 teams 配置） */
  isPlayerInTeam(teamId: number, playerId: number): boolean {
    const t = this.getTeamById(teamId);
    return !!t && t.players.some((p) => p.id === playerId);
  }

  /** 获取“当前进行中的比赛”的双方上场队员引用（无进行中比赛则返回空数组） */
  getCurrentMatchTeamsRefs(nowSec: number = Math.floor(Date.now() / 1000)): {
    teamA: PlayerRef[];
    teamB: PlayerRef[];
  } {
    nowSec = this.resolveNowSec(nowSec);
    const cur = this.getCurrentMatch(nowSec);
    if (!cur) {
      return { teamA: [], teamB: [] };
    }
    return this.getMatchPlayers(cur);
  }

  /** 返回指定玩家在当前进行中比赛中的阵营（'teamA' | 'teamB'），否则 undefined */
  getPlayerSideInCurrentMatch(
    playerId: number,
    nowSec: number = Math.floor(Date.now() / 1000)
  ): 'teamA' | 'teamB' | undefined {
    nowSec = this.resolveNowSec(nowSec);
    const cur = this.getCurrentMatch(nowSec);
    if (!cur) {
      return undefined;
    }
    if (cur.teamA.players.some((p) => p.id === playerId)) {
      return 'teamA';
    }
    if (cur.teamB.players.some((p) => p.id === playerId)) {
      return 'teamB';
    }
    return undefined;
  }

  /**
   * 对比队伍配置与当前比赛上场名单，返回差异：
   * - onStageButNotInTeam: 当前上场但不在该队伍配置里的玩家ID
   * - inTeamButNotOnStage: 队伍配置里但未上场的玩家ID
   */
  getLineupDiffForTeam(
    teamId: number,
    nowSec: number = Math.floor(Date.now() / 1000)
  ): { onStageButNotInTeam: number[]; inTeamButNotOnStage: number[] } {
    nowSec = this._nowSecOverride ?? nowSec;
    const team = this.getTeamById(teamId);
    const cur = this.getCurrentMatch(nowSec);
    if (!team || !cur) {
      return { onStageButNotInTeam: [], inTeamButNotOnStage: [] };
    }

    let stageArray: number[] = [];
    if (cur.teamA.name === team.name) {
      stageArray = cur.teamA.players.map((p) => p.id);
    } else if (cur.teamB.name === team.name) {
      stageArray = cur.teamB.players.map((p) => p.id);
    }

    const stage = new Set<number>(stageArray);
    const roster = new Set<number>(team.players.map((p) => p.id));

    const onStageButNotInTeam: number[] = [];
    const inTeamButNotOnStage: number[] = [];

    for (const id of stage) {
      if (!roster.has(id)) {
        onStageButNotInTeam.push(id);
      }
    }
    for (const id of roster) {
      if (!stage.has(id)) {
        inTeamButNotOnStage.push(id);
      }
    }
    return { onStageButNotInTeam, inTeamButNotOnStage };
  }

  // --- History & Upcoming ---
  /** 获取历史回放列表（不存在时返回空数组） */
  getHistory(): HistoryItem[] {
    return this.data.history ?? [];
  }

  /** 获取即将开始列表（不存在时返回空数组） */
  getUpcoming(): UpcomingItem[] {
    return this.data.upcoming ?? [];
  }

  /** 按开始时间升序返回历史回放 */
  getHistorySortedByStart(): HistoryItem[] {
    return [...this.getHistory()].sort((a, b) => a.startTime - b.startTime);
  }

  /** 按开始时间升序返回即将开始列表 */
  getUpcomingSortedByStart(): UpcomingItem[] {
    return [...this.getUpcoming()].sort((a, b) => a.startTime - b.startTime);
  }

  // --- Serialization ---
  /** 返回原始 JSON 数据 */
  toJSON(): LivePayload {
    return this.data;
  }

  // --- Player helpers ---
  /**
   * 根据玩家ID查找玩家引用（仅限“当前进行中的比赛”的上场队员）。
   * 未上场或无进行中比赛时返回 undefined；不再回退到队伍配置。
   */
  getPlayerRefById(
    playerId: number,
    nowSec: number = Math.floor(Date.now() / 1000)
  ): PlayerRef | undefined {
    nowSec = this.resolveNowSec(nowSec);
    const curRefs = this.getCurrentMatchPlayerRefs(nowSec);
    const inCur = curRefs.find((p) => p.id === playerId);
    if (inCur) {
      return inCur;
    }
    return undefined;
  }

  /** 赛程进度与倒计时 */
  /** 当前比赛进度（0-1），以下一场开赛时间估算。不可得时返回 undefined */
  getCurrentMatchProgress(
    nowSec: number = Math.floor(Date.now() / 1000)
  ): number | undefined {
    nowSec = this.resolveNowSec(nowSec);
    const idx = this.getCurrentMatchIndex(nowSec);
    if (idx === null || idx === undefined || idx < 0) {
      return undefined;
    }
    const matches = this.getMatches();
    const cur = matches[idx];
    if (!cur) {
      return undefined;
    }
    const curStartSec = cur.startTime || 0;
    const next = matches[idx + 1];
    const nextStartSec = next ? next.startTime || 0 : undefined;
    if (!nextStartSec || nextStartSec <= curStartSec) {
      return undefined;
    }
    const progress = (nowSec - curStartSec) / (nextStartSec - curStartSec);
    return Math.max(0, Math.min(1, progress));
  }

  /** 距离下一场比赛的倒计时（秒）。不可得时返回 undefined */
  getCountdownToNextMatch(
    nowSec: number = Math.floor(Date.now() / 1000)
  ): number | undefined {
    nowSec = this.resolveNowSec(nowSec);
    const idx = this.getCurrentMatchIndex(nowSec);
    if (idx === null || idx < 0) {
      return undefined;
    }
    const matches = this.getMatches();
    const next = matches[idx + 1];
    if (!next) {
      return undefined;
    }
    const deltaSec = (next.startTime || 0) - nowSec;
    return Math.max(0, deltaSec);
  }
}
